package com.example.study.jetpack.compose

import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp

/**
 *  created by 20300
 *   on 2022/3/14
 *   describe:https://blog.csdn.net/Mr_Tony/article/details/118892867?spm=1001.2014.3001.5502
 */
class Compose的CompositionLocal {
    /*前言
    在Compose中定义函数的话如果在行参中使用业务变量的话,会使整个业务慢慢变得复杂,难以维护,也不利于解耦的目睹.
    举个例子,一个布局嵌套了七八层,然后在低层有一个变量,然后会影响最顶层的组合.但是中间的组合并不使用这个变量.
    通常处理无非是两种方式,定义全局变量或者通过函数一层一层发送.但都不太好,因此官方提供了CompositionLocal
    来处理这种组合分层的问题
    官方解释如下:
    Compose将数据通过组合树显式地通过参数传递给可组合函数.这通常是让数据流过树的最简单最好的方法
    有时,对于去多组件需要的数据,或者当组件需要在彼此之间传递数据但可以保证该实现细节私有时,此模型科恒很麻烦或
    分解,对于这些情况,CompositionLocal需要将的值限定为组合的特定子层结构时,它们是有意义的.
    必须创建一个CompositionLocal实例,该实例可以被消费者静态引用.CompositionLocal实例本省不持有任何数据,
    可以将其视为传递到树种的数据的类型安全标识符.CompositionLocal工厂函数采用单个参数:
    在Composition没有提供程序的情况下使用a的情况下创建默认值的工厂.如果这是你不想处理的情况,则可以在此工厂中
    引发错误.
    在树上的某个地方,CompositionLocalProvider可以使用一个组件,它为CompositionLocal,这通常位于树的根,
    但也可以在任何地方,也可以在多个位置使用以覆盖子树的提供值.
    中间组件不需要知道该CompositionLocal值,并且可以对其具有零依赖关系
    */


    //用来演示组合分层的问题
    class CompositionLocalActivity:  AppCompatActivity(){
        val ActiveUser = compositionLocalOf<String> { error("No active user found!") }//这个需要被定义为可被静态引用的实例或者被@Compose修饰的公共组合中，如果没有提供值的话会报错误
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            val text = "你好啊!!!"
            setContent {

                body(text)
            }
        }
        @Composable
        fun body(text: String){
            val text2 = staticCompositionLocalOf<String> { "" }//比如在这里定义也是可以的，这样就避免了全局静态变量的那种很糟糕的编码体验，同使也保留了组合隔离的特性
            CompositionLocalProvider(ActiveUser provides text) {
            //CompositionLocalProvider(text2     provides text) {
                Column {
                    content()
                }
            }
        }

        @Composable
        private fun content(){
            child()
        }

        @Composable
        private fun child(){//在这里并不需要知道传进来的值，所以可以很方便的保持组件的独立性，当然这个也可以隔好几层，只要在使用前定义就好
            val text = ActiveUser.current
            Text(text)
        }

    }

    /*compositionLocalOf与statiComposeitionLocalOf区别
    https://compose.net.cn/design/theme/understanding_material_theme/
    简单概括,当选择使用staticCompositionLocalOf时,实际上创建了个StaticProvidable实例,
    当其所提供的值改变时,会导致CompositionLocalProvide内部所有composable触发重组(recompose)
    如果使用compositionLocalOf,实际上创建了个DynamicProvidableCompositionLocal实例,当其
    所提供的值改变时,仅会导致CompositionLocalProvide内部依赖当前CompositionLocal的
    composable触发重组(recompose)
    */
    var isStatic = false
    var compositionLocalName = ""
    val currentLocalColor = if (isStatic) {
        compositionLocalName = "StaticCompositionLocal 场景"
        staticCompositionLocalOf { Color.Black }
    } else {
        compositionLocalName = "DynamicCompositionLocal 场景"
        compositionLocalOf { Color.Black }
    }

    var recomposeFlag = "Init"
    @Preview
    @Composable
    fun CompositionLocalDemo(isStatic: Boolean = false) {
        var color by remember{ mutableStateOf(Color.Green) }
        Box(
            modifier = Modifier.fillMaxSize(),
            contentAlignment = Alignment.Center
        ) {
            Column(horizontalAlignment = Alignment.CenterHorizontally){
                Text(text = "${compositionLocalName}")
                Spacer (Modifier.height(20.dp))
                CompositionLocalProvider(
                    currentLocalColor provides color
                ) {
                    TaggedBox("Wrapper: ${recomposeFlag}", 400.dp,Color.Red) {
                        TaggedBox("Middle: ${recomposeFlag}", 300.dp, currentLocalColor.current) {
                            TaggedBox("Inner: ${recomposeFlag}", 200.dp, Color.Yellow)
                        }
                    }
                }
                Spacer (Modifier.height(20.dp))
                Button(
                    onClick = {
                        color = Color.Blue
                    }
                ) {
                    Text(text = "Change Theme")
                }
            }
        }
        recomposeFlag = "Recompose"
    }

    @Composable
    fun TaggedBox(tag:String, size: Dp, background: Color, content: @Composable () -> Unit = {}) {
        Column(
            modifier = Modifier
                .size(size)
                .background(background),
            horizontalAlignment = Alignment.CenterHorizontally) {
            Text(text = tag)
            Box(
                modifier = Modifier.fillMaxSize(),
                contentAlignment = Alignment.Center
            ) {
                content()
            }
        }
    }

    /*当数据不大可能会改变时候或者永远不会改变时候使用staticCompositionLocalOf会更有效.
    例如Android上下文对象,字体加载器,或者共享元素等.如果是那些容易改变的,使用compositionLocalOf会更好一些,
    如动画,主题等等.大致原因是,类似与字体加载器这些源文件发生改变后,难以监测到,所以使用staticCompositionLocalOf
    进行UI重组会比较好
    */

    /*https://developer.android.google.cn/reference/kotlin/androidx/compose/runtime/CompositionLocal
    https://compose.net.cn/design/theme/understanding_material_theme/
    https://developer.android.google.cn/reference/kotlin/androidx/compose/runtime/package-summary#staticCompositionLocalOf(kotlin.Function0)
    https://medium.com/mobile-app-development-publication/android-jetpack-compose-compositionlocal-made-easy-8632b201bfcd
    */

}